package cn.kpt.trade.controller;

import cn.kpt.beans.dto.Dto;
import cn.kpt.beans.pojo.ItripHotelOrder;
import cn.kpt.trade.service.HotelOrderService;
import cn.kpt.trade.utils.MyConfig;
import cn.kpt.trade.utils.WXPay;
import cn.kpt.trade.utils.WXPayConstants;
import cn.kpt.trade.utils.WXPayUtil;
import cn.kpt.utils.DtoUtil;
import cn.kpt.utils.EmptyUtils;
import cn.kpt.utils.ErrorCode;
import lombok.Cleanup;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @version 1.0
 * @ClassName cn.kpt.trade.controller.WxPayController
 * @Description TODO
 * @Author kangpengtao
 * @Date 2019/10/30 11:20
 */
@Controller
@RequestMapping("/api/wxpay")
public class WxPayController {
    @Resource
    private MyConfig myConfig;
    @Resource
    private HotelOrderService hotelOrderService;
    /**
     * 微信支付页面-订单状态查询
     * @param orderNo
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/queryorderstatus/{orderNo}",method = RequestMethod.GET)
    public Dto doQueryOrderStatus(@PathVariable String orderNo){
        try {
            ItripHotelOrder itripHotelOrder = hotelOrderService.getOrderByOrderNo(orderNo);
            return DtoUtil.returnDataSuccess(itripHotelOrder);
        } catch (Exception e) {
            e.printStackTrace();
            return DtoUtil.returnFail("查询订单状态失败！", ErrorCode.TRADE_UNKNOWN);
        }
    }
    /**
     * 微信支付-生成预支付交易单(二维码)
     *
     * @param orderNo
     * @param request
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/createqccode/{orderNo}", method = RequestMethod.GET)
    public Dto doCreateQcCode(@PathVariable String orderNo, HttpServletRequest request) {
        try {
            ItripHotelOrder itripHotelOrder = hotelOrderService.getOrderByOrderNo(orderNo);
            // 验证订单是否存在
            if (EmptyUtils.isEmpty(itripHotelOrder)) {
                return DtoUtil.returnFail("订单不存在！", ErrorCode.TRADE_PARAMETER_ERROR);
            }
            // 验证订单是否已经结束
            if (itripHotelOrder.getOrderStatus() != 0) {
                return DtoUtil.returnFail("订单已经结束！", ErrorCode.TRADE_ORDER_ALREADY_OVER);
            }

            WXPay wxpay = new WXPay(myConfig);
            Map<String, String> data = new HashMap<String, String>();
            // 商品描述  商品简单描述，该字段请按照规范传递，具体请见参数规定
            // 浏览器打开的网站主页title名 -商品概述
            data.put("body", "爱旅行-酒店支付");
            // 商户订单号 商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|* 且在同一个商户号下唯一。详见商户订单号
            // 商户支付的订单号由商户自定义生成，
            // 仅支持使用字母、数字、中划线-、下划线_、竖线|、星号*这些英文半角字符的组合，
            // 请勿使用汉字或全角等特殊字符。
            // 微信支付要求商户订单号保持唯一性（建议根据当前系统时间加随机序列来生成订单号）。
            // 重新发起一笔支付要使用原订单号，避免重复支付；
            // 已支付过或已调用关单、撤销（请见后文的API列表）的订单号不能重新发起支付。
            data.put("out_trade_no", orderNo);
            // 设备号 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"
            data.put("device_info", "WEB");
            // 标价币种 符合ISO 4217标准的三位字母代码，默认人民币：CNY，详细列表请参见货币类型
            // 境内商户号仅支持人民币
            data.put("fee_type", "CNY");
            // 标价金额  订单总金额，单位为分，详见支付金额
            //交易金额默认为人民币交易，接口中参数支付金额单位为【分】，参数值不能带小数。对账单中的交易金额单位为【元】。
            //外币交易的支付金额精确到币种的最小单位，参数值不能带小数点。
            data.put("total_fee", "1"); // itripHotelOrder.getPayAmount()
            // 终端IP 支持IPV4和IPV6两种格式的IP地址。用户的客户端IP
            data.put("spbill_create_ip", request.getRemoteAddr());
            // 通知地址 异步接收微信支付结果通知的回调地址，通知url必须为【外网可访问的url】，不能携带参数。
            // 测试阶段可借用 花生壳 实现内网穿透
            data.put("notify_url", "http://26898ne596.zicp.vip/trade/api/wxpay/notify");
            // 交易类型 JSAPI -JSAPI支付  NATIVE -Native支付 APP -APP支付
            data.put("trade_type", "NATIVE");  // 此处指定为扫码支付
            // 商品ID trade_type=NATIVE时，此参数必传。此参数为二维码中包含的商品ID，商户自行定义。
            data.put("product_id", itripHotelOrder.getRoomId() + "");
            // 签名类型，默认为MD5，支持HMAC-SHA256和MD5。
            data.put("sign_type", WXPayConstants.HMACSHA256);
            // 发起统一下单请求
            Map<String, String> resp = wxpay.unifiedOrder(data);

            // 结果  返回总金额  和  二维码链接
            Map<String, Object> result = new HashMap<>();
            if (resp.get("return_code").equals(WXPayConstants.SUCCESS)
                    && resp.get("result_code").equals(WXPayConstants.SUCCESS)) {
                //  二维码链接 trade_type=NATIVE时有返回，此url用于生成支付二维码，然后提供给用户进行扫码支付。
                result.put("codeUrl", resp.get("code_url"));
                result.put("payAmount", itripHotelOrder.getPayAmount());
                return DtoUtil.returnDataSuccess(result);
            } else {
                // 失败
                return DtoUtil.returnFail(resp.get("retrun_msg"), ErrorCode.TRADE_RESULT_ERROR);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return DtoUtil.returnFail("获取支付二维码失败！", ErrorCode.TRADE_UNKNOWN);
        }
    }
    /**
     * 微信支付-支付结果通知
     *
     * @param request
     * @param response
     */
    @RequestMapping(value = "/notify", method = RequestMethod.POST)
    public void doNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            // 接收数据流信息
            @Cleanup ServletInputStream is = request.getInputStream();
            // BufferedReader br = new BufferedReader(new InputStreamReader(is));
            // 定义Buffer存储数据流字符串
            StringBuffer notifyBuffer = new StringBuffer();
            byte[] bytes = new byte[1024];
            int len = 0;
            System.out.println("123");
            while ((len = is.read(bytes)) != -1) {
                notifyBuffer.append(new String(bytes, 0, len));
            }
            // 将数据流字符串转换为map集合
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyBuffer.toString());
            // 1.商户系统对于支付结果通知的内容一定要做签名验证,
            // 并校验返回的订单金额是否与商户侧的订单金额一致，
            // 防止数据泄漏导致出现“假通知”，造成资金损失。
            WXPay wxpay = new WXPay(myConfig);
            // 签名验证
            if(wxpay.isPayResultNotifySignatureValid(notifyMap)){
                // 验证是否交易成功
                if(notifyMap.get("return_code").equals(WXPayConstants.SUCCESS)
                        && notifyMap.get("result_code").equals(WXPayConstants.SUCCESS)){
                    // 2.当收到通知进行处理时，首先检查对应业务数据的状态，
                    // 判断该通知是否已经处理过，如果没有处理过再进行处理，如果处理过直接返回结果成功
                    if(hotelOrderService.checkOrderStatus(notifyMap)){
                        // 3.更改订单状态
                        hotelOrderService.updateOrder(notifyMap);
                    }
                }
            }
            /*
            后台通知交互时，如果微信收到商户的应答不符合规范或超时，微信会判定本次通知失败，重新发送通知，
            直到成功为止（在通知一直不成功的情况下，微信总共会发起多次通知，
            通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m），但微信不保证通知最终一定能成功。
             */
            // 支付完成后，微信会把相关支付结果及用户信息通过数据流的形式发送给商户，商户需要接收处理，并按文档规范返回应答。
            Map<String, String> resultMap = new HashMap<>();
            resultMap.put("return_code", WXPayConstants.SUCCESS);
            resultMap.put("return_msg", "OK");
            response.getWriter().print(WXPayUtil.mapToXml(resultMap));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
